home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / scan.py < prev    next >
Text File  |  2008-10-13  |  53KB  |  1,410 lines

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19. #
  20. # Author: Don Welch
  21. #
  22.  
  23. from __future__ import division
  24.  
  25. __version__ = '1.0'
  26. __title__ = 'Scan Utility'
  27. __doc__ = "SANE-based scan utility for HPLIP." 
  28.  
  29. # Std Lib
  30. import sys
  31. import os
  32. import os.path
  33. import getopt
  34. import signal
  35. import time
  36. import socket
  37.  
  38. # Local
  39. from base.g import *
  40. from base import tui
  41. import base.utils as utils
  42. from base import device
  43. from prnt import cups
  44.  
  45. log.set_module('hp-scan')
  46.  
  47. prop.prog = sys.argv[0]
  48.  
  49. device_uri = None
  50. username = prop.username
  51. mode = GUI_MODE
  52. mode_specified = False
  53. res = 300
  54. scan_mode = 'color'
  55. tlx = None
  56. tly = None
  57. brx = None
  58. bry = None
  59. units = "mm"
  60. output = ''
  61. dest = []
  62. email_from = ''
  63. email_to = []
  64. email_subject = 'hp-scan from %s' % socket.gethostname()
  65. email_note = ''
  66. fax = ''
  67. resize = 100
  68. contrast = 0
  69. brightness = 0
  70. printer = ''
  71. page_size = ''
  72. size_desc = ''
  73. page_units = 'mm'
  74. valid_res = (75, 150, 300, 600, 1200, 2400, 4800)
  75. default_res = 300
  76. scanner_compression = 'JPEG'
  77. adf = False
  78.  
  79. PAGE_SIZES = { # in mm
  80.     '5x7' : (127, 178, "5x7 photo", 'in'),
  81.     '4x6' : (102, 152, "4x6 photo", 'in'),
  82.     '3x5' : (76, 127, "3x5 index card", 'in'),
  83.     'a2_env' : (111, 146, "A2 Envelope", 'in'),
  84.     'a3' : (297, 420, "A3", 'mm'),
  85.     "a4" : (210, 297, "A4", 'mm'),
  86.     "a5" : (148, 210, "A5", 'mm'),
  87.     "a6" : (105, 148, "A6", 'mm'),
  88.     "b4" : (257, 364, "B4", 'mm'),
  89.     "b5" : (182, 257, "B5", 'mm'),
  90.     "c6_env" : (114, 162, "C6 Envelope", 'in'),
  91.     "dl_env" : (110, 220, "DL Envelope", 'in'),
  92.     "exec" : (184, 267, "Executive", 'in'),
  93.     "flsa" : (216, 330, "Flsa", 'mm'),
  94.     "higaki" : (100, 148, "Hagaki", 'mm'),
  95.     "japan_env_3" : (120, 235, "Japanese Envelope #3", 'mm'),
  96.     "japan_env_4" : (90, 205, "Japanese Envelope #4", 'mm'),
  97.     "legal" : (215, 356, "Legal", 'in'),
  98.     "letter" : (215, 279, "Letter", 'in'),
  99.     "no_10_env" : (105, 241, "Number 10 Envelope", 'in'),
  100.     "oufufu-hagaki" : (148, 200, "Oufuku-Hagaki", 'mm'),
  101.     "photo" : (102, 152, "Photo", 'in'),
  102.     "super_b" : (330, 483, "Super B", 'in'),
  103.     }
  104.  
  105. USAGE = [(__doc__, "", "name", True),
  106.          ("Usage: hp-scan [SANE-DEVICE-URI] [MODE] [-n OPTIONS] [OPTIONS]", "", "summary", True),
  107.          ("[SANE-DEVICE-URI]", "", "header", False),
  108.          ("SANE device URI:", "-d<sane_device_uri> or --device=<sane_device_uri>", "option", False),
  109.          ("", "URI format: hpaio:/<bus>/<model>?<identification>", "option", False),
  110.  
  111.          utils.USAGE_SPACE,
  112.          ("[MODE]", "", "header", False),
  113.          ("Enter graphical UI mode:", "-u or --gui (Default)", "option", False),
  114.          ("Run in non-interactive mode:", "-n or --non-interactive", "option", False),
  115.  
  116.          utils.USAGE_SPACE,
  117.          ("[-n OPTIONS] (General) (Not applicable to GUI mode)", "", "header", False),
  118.          ("Scan destinations:", "-s<dest_list> or --dest=<dest_list>", "option", False),
  119.          ("", "where <dest_list> is a comma separated list containing one or more of: 'file'\*, ", "option", False),
  120.          ("", "'viewer', 'editor', 'pdf', 'fax', or 'print'. Use only commas between values, no spaces.", "option", False), 
  121.          ("Scan mode:", "-m<mode> or --mode=<mode>. Where <mode> is 'color'\*, 'gray' or 'lineart'.", "option", False),
  122.          ("Scanning resolution:", "-r<resolution_in_dpi> or --res=<resolution_in_dpi> or --resolution=<resolution_in_dpi>", "option", False),
  123.          ("", "where <resolution_in_dpi> is %s (300 is default)." % ', '.join([str(x) for x in valid_res]), "option", False),
  124.          ("Image resize:", "--resize=<scale_in_%> (min=1%, max=400%, default=100%)", "option", False),
  125.          ("Image contrast:", "--contrast=<contrast>", "option", False),
  126.          ("ADF mode (EXPERIMENTAL):", "--adf (Note, only PDF output is supported when using the ADF)", "option", False),
  127.  
  128.          utils.USAGE_SPACE,
  129.          ("[-n OPTIONS] (Scan area) (Not applicable to GUI mode)", "", "header", False),
  130.          ("Specify the units for area/box measurements:", "-t<units> or --units=<units>", "option", False),
  131.          ("", "where <units> is 'mm'\*, 'cm', 'in', 'px', or 'pt' ('mm' is default).", "option", False),
  132.          ("Scan area:", "-a<tlx>,<tly>,<brx>,<bry> or --area=<tlx>,<tly>,<brx>,<bry>", "option", False),
  133.          ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False),
  134.          ("", "Units for tlx, tly, brx, and bry are specified by -t/--units (default is 'mm').", "option", False),
  135.          ("", "Use only commas between values, no spaces.", "option", False),
  136.          ("Scan box:", "--box=<tlx>,<tly>,<width>,<height>", "option", False),
  137.          ("", "tlx and tly coordinates are relative to the upper left corner of the scan area.", "option", False),
  138.          ("", "Units for tlx, tly, width, and height are specified by -t/--units (default is 'mm').", "option", False),         
  139.          ("", "Use only commas between values, no spaces.", "option", False),
  140.          ("Top left x of the scan area:", "--tlx=<tlx>", "option", False),
  141.          ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False),
  142.          ("", "Units are specified by -t/--units (default is 'mm').", "option", False),
  143.          ("Top left y of the scan area:", "--tly=<tly>", "option", False),
  144.          ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False),
  145.          ("", "Units are specified by -t/--units (default is 'mm').", "option", False),
  146.          ("Bottom right x of the scan area:", "--brx=<brx>", "option", False),
  147.          ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False),
  148.          ("", "Units are specified by -t/--units (default is 'mm').", "option", False),
  149.          ("Bottom right y   of the scan area:", "--bry=<bry>", "option", False),
  150.          ("", "Coordinates are relative to the upper left corner of the scan area.", "option", False),
  151.          ("", "Units are specified by -t/--units (default is 'mm').", "option", False),
  152.          ("Specify the scan area based on a paper size:", "--size=<paper size name>", "option", False),
  153.          ("", "where <paper size name> is one of: %s" % ', '.join(PAGE_SIZES.keys()), "option", False), 
  154.  
  155.          utils.USAGE_SPACE,
  156.          ("[-n OPTIONS] ('file' dest) (Not applicable to GUI mode)", "", "header", False),
  157.          ("Filename for 'file' destination:", "-o<file> or -f<file> or --file=<file> or --output=<file>", "option", False),
  158.  
  159.          utils.USAGE_SPACE,
  160.          ("[-n OPTIONS] ('pdf' dest) (Not applicable to GUI mode)", "", "header", False),
  161.          ("PDF viewer application:", "--pdf=<pdf_viewer>", "option", False),
  162.  
  163.          utils.USAGE_SPACE,
  164.          ("[-n OPTIONS] ('viewer' dest) (Not applicable to GUI mode)", "", "header", False),
  165.          ("Image viewer application:", "-v<viewer> or --viewer=<viewer>", "option", False),
  166.          utils.USAGE_SPACE,
  167.  
  168.          ("[-n OPTIONS] ('editor' dest) (Not applicable to GUI mode)", "", "header", False),
  169.          ("Image editor application:", "-e<editor> or --editor=<editor>", "option", False),
  170.          utils.USAGE_SPACE,
  171.  
  172.          ("[-n OPTIONS] ('email' dest) (Not applicable to GUI mode)", "", "header", False),
  173.          ("From: address for 'email' dest:", "--email-from=<email_from_address> (required for 'email' dest.)", "option", False),
  174.  
  175.          ("To: address for 'email' dest:", "--email-to=<email__to_address> (required for 'email' dest.)", "option", False),
  176.          ("Email subject for 'email' dest:", '--email-subject="<subject>"', "option", False),
  177.          ("", 'Use double quotes (") around the subject if it contains space characters.', "option", False),
  178.          ("Note or message for the 'email' dest:", '--email-msg="<msg>" or --email-note="<note>"', "option", False),
  179.          ("", 'Use double quotes (") around the note/message if it contains space characters.', "option", False),
  180.  
  181.          utils.USAGE_SPACE,
  182.          ("[-n OPTIONS] ('fax' dest) (Not applicable to GUI mode)", "", "header", False),
  183.          ("Fax queue/printer:", "--fax=<fax_printer_name>", "option", False),
  184.          utils.USAGE_SPACE,
  185.          ("[-n OPTIONS] ('printer' dest) (Not applicable to GUI mode)", "", "header", False),
  186.          ("Printer queue/printer:", "--printer=<printer_name>", "option", False),
  187.          utils.USAGE_SPACE,
  188.          ("[-n OPTIONS] (advanced) (Not applicable to GUI mode)", "", "header", False),
  189.          ("Set the scanner compression mode:", "-x<mode> or --compression=<mode>, <mode>='raw', 'none' or 'jpeg' ('jpeg' is default) ('raw' and 'none' are equivalent)", "option", False),
  190.          utils.USAGE_SPACE,
  191.          utils.USAGE_OPTIONS,
  192.          utils.USAGE_LOGGING1, utils.USAGE_LOGGING2, utils.USAGE_LOGGING3,
  193.          utils.USAGE_HELP,
  194.          utils.USAGE_NOTES,
  195.          ("1. If no dest is provided, the 'file' dest will be automatically invoked.", "", "note", False),
  196.          ("2. If applications for viewer, editor, or pdf viewer are not provided, reasonable defaults will be used.", "", "note", False),
  197.          ("3. If --printer is not specified, the CUPS default will be used if available.", "", "note", False),
  198.          ("4. If an output file is not specified with the 'file' dest, a reasonable default will be used.", "", "note", False),
  199.          ("5. Some options may not be valid on some scanning devices.", "", "note", False),
  200.          ("6. The following features are not yet implemented: GUI mode, batch scanning, film/negative scanning, contrast adjustment, brightness adjustment, autocrop, resize to axb, resize to xKB, ", "", "note", False),
  201.  
  202.          utils.USAGE_SPACE,
  203.          utils.USAGE_EXAMPLES,
  204.          ("Quickly (low-res) scan entire page in color to file:", "$ hp-scan -n -r75",  "example", False),
  205.          ("Scan upper left 1in corner and send as email:", '$ hp-scan -n --box=0,0,1,1 -tin -semail --email-from=foo@bar.org --email-to=bar@foo.org --email-note="Test scan" --email-subject="Test scan email"', "example", False),
  206.          ("Scan entire page in 300dpi grayscale and then edit:", "$ hp-scan -n -seditor -mgray",  "example", False),
  207.          ("Launch the hp-scan GUI:", "$ hp-scan",  "example", False),
  208.          ("Scan into The GIMP:", "$ hp-scan -n --editor=gimp",  "example", False),
  209.         ]
  210.  
  211. def usage(typ='text'):
  212.     if typ == 'text':
  213.         utils.log_title(__title__, __version__)
  214.  
  215.     utils.format_text(USAGE, typ, __title__, 'hp-scan', __version__)
  216.     sys.exit(0)
  217.  
  218. try:
  219.     viewer = ''
  220.     viewer_list = ['kview', 'display', 'gwenview', 'eog', 'kuickshow',]
  221.     for v in viewer_list:
  222.         vv = utils.which(v)
  223.         if vv:
  224.             viewer = os.path.join(vv, v)
  225.             break
  226.  
  227.  
  228.     editor = ''
  229.     editor_list = ['kolourpaint', 'gimp', 'krita', 'cinepaint', 'mirage',]
  230.     for e in editor_list:
  231.         ee = utils.which(e)
  232.         if ee:
  233.             editor = os.path.join(ee, e)
  234.             break
  235.  
  236.     pdf_viewer = ''
  237.     pdf_viewer_list = ['kpdf', 'acroread', 'xpdf', 'evince',]
  238.     for v in pdf_viewer_list:
  239.         vv = utils.which(v)
  240.         if vv:
  241.             pdf_viewer = os.path.join(vv, v)
  242.             break
  243.  
  244.     try:
  245.         opts, args = getopt.getopt(sys.argv[1:],'l:hd:p:b:gunr:m:t:o:s:f:v:e:c:a:x:', 
  246.             ['device=', 'printer=', 'level=', 
  247.              'help', 'help-rest', 
  248.              'help-man', 'logfile=', 
  249.              'gui', 'non-interactive', 'logging=',
  250.               'help-desc', 'resolution=', 'res=', 'mode=',
  251.               'tlx=', 'tly=', 'brx=', 'bry=', 'units=', 
  252.               'area=', 'box=', 'output=', 'dest=', 'destination=',
  253.               'file=', 'pdf=', 'viewer=', 'editor=',
  254.               'email-from=', 'email-to=', 'resize=',
  255.               'email-subject=', 'email-note=', 'email-msg=',
  256.               'contrast=', 'brightness=', 'size=', 'compression=',
  257.               'adf', 'fax='])
  258.  
  259.  
  260.  
  261.     except getopt.GetoptError, e:
  262.         log.error(e.msg)
  263.         usage()
  264.         sys.exit(1)
  265.  
  266.     if os.getenv("HPLIP_DEBUG"):
  267.         log.set_level('debug')
  268.  
  269.     for o, a in opts:
  270.         if o in ('-l', '--logging'):
  271.             log_level = a.lower().strip()
  272.             if not log.set_level(log_level):
  273.                 usage()
  274.  
  275.         elif o == '-g':
  276.             log.set_level('debug')
  277.  
  278.         elif o in ('-h', '--help'):
  279.             usage()
  280.  
  281.         elif o == '--help-rest':
  282.             usage('rest')
  283.  
  284.         elif o == '--help-man':
  285.             usage('man')
  286.  
  287.         elif o == '--help-desc':
  288.             print __doc__,
  289.             sys.exit(0)
  290.  
  291.         elif o in ('-d', '--device'):
  292.             device_uri = a
  293.  
  294.         elif o in ('-n', '--non-interactive'):
  295.             if mode_specified:
  296.                 log.error("You may only specify a single mode as a parameter (-n or -u).")
  297.                 sys.exit(1)
  298.  
  299.             mode = NON_INTERACTIVE_MODE
  300.             mode_specified = True
  301.  
  302.         elif o in ('-u', '--gui'):
  303.             if mode_specified:
  304.                 log.error("You may only specify a single mode as a parameter (-n or -u).")
  305.                 sys.exit(1)
  306.  
  307.             mode = GUI_MODE
  308.             mode_specified = True
  309.  
  310.         elif o in ('-x', '--compression'):
  311.             a = a.strip().lower()
  312.  
  313.             if a in ('jpeg', 'jpg'):
  314.                 scanner_compression = 'JPEG'
  315.  
  316.             elif a in ('raw', 'none'):
  317.                 scanner_compression = 'None'
  318.  
  319.             else:
  320.                 log.error("Invalid compression value. Valid values are 'jpeg', 'raw', and 'none'.")
  321.                 log.error("Using default value of 'jpeg'.")
  322.                 scanner_compression = 'JPEG'
  323.  
  324.         elif o in ('-m', '--mode'):
  325.             a = a.strip().lower()
  326.  
  327.             if a in ('color', 'colour'):
  328.                 scan_mode = 'color'
  329.  
  330.             elif a in ('lineart', 'bw', 'b&w'):
  331.                 scan_mode = 'lineart'
  332.  
  333.             elif a in ('gray', 'grayscale', 'grey', 'greyscale'):
  334.                 scan_mode = 'gray'
  335.  
  336.             else:
  337.                 log.error("Invalid mode. Using default of 'color'.")
  338.                 log.error("Valid modes are 'color', 'lineart', or 'gray'.")
  339.                 scan_mode = 'color'
  340.  
  341.         elif o in ('--res', '--resolution', '-r'):
  342.             try:
  343.                 r = int(a.strip())
  344.             except ValueError:
  345.                 log.error("Invalid resolution. Using default of %s dpi." % default_res)
  346.                 log.error("Valid resolutions are %s dpi." % ', '.join([str(x) for x in valid_res]))
  347.                 res = default_res
  348.             else:
  349.                 if r in valid_res: 
  350.                     res = r
  351.                 else:
  352.                     res = valid_res[0]
  353.                     min_dist = sys.maxint
  354.                     for x in valid_res: 
  355.                         if abs(r-x) < min_dist:
  356.                             min_dist = abs(r-x)
  357.                             res = x
  358.  
  359.                     log.warn("Invalid resolution. Using closest valid resolution of %d dpi" % res)
  360.                     log.error("Valid resolutions are %s dpi." % ', '.join([str(x) for x in valid_res]))
  361.  
  362.         elif o in ('-t', '--units'):
  363.             a = a.strip().lower()
  364.  
  365.             if a in ('in', 'inch', 'inches'):
  366.                 units = 'in'
  367.  
  368.             elif a in ('mm', 'milimeter', 'milimeters', 'millimetre', 'millimetres'):
  369.                 units = 'mm'
  370.  
  371.             elif a in ('cm', 'centimeter', 'centimeters', 'centimetre', 'centimetres'):
  372.                 units = 'cm'
  373.  
  374.             elif a in ('px', 'pixel', 'pixels', 'pel', 'pels'):
  375.                 units = 'px'
  376.  
  377.             elif a in ('pt', 'point', 'points'):
  378.                 units = 'pt'
  379.  
  380.             else:
  381.                 log.error("Invalid units. Using default of mm.")
  382.                 units = 'mm'
  383.  
  384.         elif o == '--tlx':
  385.             a = a.strip().lower()
  386.             try:
  387.                 f = float(a)
  388.             except ValueError:
  389.                 log.error("Invalid value for tlx.")
  390.             else:
  391.                 tlx = f
  392.  
  393.         elif o == '--tly':
  394.             a = a.strip().lower()
  395.             try:
  396.                 f = float(a)
  397.             except ValueError:
  398.                 log.error("Invalid value for tly.")
  399.             else:
  400.                 tly = f
  401.  
  402.         elif o == '--brx':
  403.             a = a.strip().lower()
  404.             try:
  405.                 f = float(a)
  406.             except ValueError:
  407.                 log.error("Invalid value for brx.")
  408.             else:
  409.                 brx = f
  410.  
  411.         elif o == '--bry':
  412.             a = a.strip().lower()
  413.             try:
  414.                 f = float(a)
  415.             except ValueError:
  416.                 log.error("Invalid value for bry.")
  417.             else:
  418.                 bry = f
  419.  
  420.         elif o in ('-a', '--area'): # tlx, tly, brx, bry
  421.             a = a.strip().lower()
  422.             try:
  423.                 tlx, tly, brx, bry = a.split(',')[:4]
  424.             except ValueError:
  425.                 log.error("Invalid scan area. Using defaults.")
  426.             else:
  427.                 try:
  428.                     tlx = float(tlx)
  429.                 except ValueError:
  430.                     log.error("Invalid value for tlx. Using defaults.")
  431.                     tlx = None
  432.  
  433.                 try:
  434.                     tly = float(tly)
  435.                 except ValueError:
  436.                     log.error("Invalid value for tly. Using defaults.")
  437.                     tly = None
  438.  
  439.                 try:
  440.                     brx = float(brx)
  441.                 except ValueError:
  442.                     log.error("Invalid value for brx. Using defaults.")
  443.                     brx = None
  444.  
  445.                 try:
  446.                     bry = float(bry)
  447.                 except ValueError:
  448.                     log.error("Invalid value for bry. Using defaults.")
  449.                     bry = None
  450.  
  451.         elif o == '--box': # tlx, tly, w, h
  452.             a = a.strip().lower()
  453.             try:
  454.                 tlx, tly, width, height = a.split(',')[:4]
  455.             except ValueError:
  456.                 log.error("Invalid scan area. Using defaults.")
  457.             else:
  458.                 try:
  459.                     tlx = float(tlx)
  460.                 except ValueError:
  461.                     log.error("Invalid value for tlx. Using defaults.")
  462.                     tlx = None
  463.  
  464.                 try:
  465.                     tly = float(tly)
  466.                 except ValueError:
  467.                     log.error("Invalid value for tly. Using defaults.")
  468.                     tly = None
  469.  
  470.                 if tlx is not None:
  471.                     try:
  472.                         brx = float(width) + tlx
  473.                     except ValueError:
  474.                         log.error("Invalid value for width. Using defaults.")
  475.                         brx = None
  476.                 else:
  477.                     log.error("Cannot calculate brx since tlx is invalid. Using defaults.")
  478.                     brx = None
  479.  
  480.                 if tly is not None:
  481.                     try:
  482.                         bry = float(height) + tly
  483.                     except ValueError:
  484.                         log.error("Invalid value for height. Using defaults.")
  485.                         bry = None    
  486.                 else:
  487.                     log.error("Cannot calculate bry since tly is invalid. Using defaults.")
  488.                     bry = None
  489.  
  490.         elif o == '--size':
  491.             size = a.strip().lower()
  492.             if size in PAGE_SIZES:
  493.                 brx, bry, size_desc, page_units = PAGE_SIZES[size]
  494.                 tlx, tly = 0, 0
  495.                 page_size = size
  496.             else:
  497.                 log.error("Invalid page size. Valid page sizes are: %s" % ', '.join(PAGE_SIZES.keys()))
  498.                 log.error("Using defaults.")
  499.  
  500.         elif o in ('-o', '--output', '-f', '--file'):
  501.             output = os.path.abspath(os.path.normpath(os.path.expanduser(a.strip())))
  502.  
  503.             try:
  504.                 ext = os.path.splitext(output)[1]
  505.             except IndexError:
  506.                 log.error("Invalid filename extension.")
  507.                 output = ''
  508.                 if 'file' in dest:
  509.                     dest.remove('file')
  510.             else:
  511.                 if ext.lower() not in ('.jpg', '.png'):
  512.                     log.error("Only JPG (.jpg) and PNG (.png) output files are supported.")
  513.                     output = ''
  514.                     if 'file' in dest:
  515.                         dest.remove('file')
  516.                 else:
  517.                     if os.path.exists(output):
  518.                         log.warn("Output file '%s' exists. File will be overwritten." % output)
  519.  
  520.                     if 'file' not in dest:
  521.                         dest.append('file')
  522.  
  523.         elif o in ('-s', '--dest', '--destination'):
  524.             a = a.strip().lower().split(',')
  525.             for aa in a:
  526.                 aa = aa.strip()
  527.                 if aa in ('file', 'fax', 'viewer', 'editor', 'printer', 'print', 'email', 'pdf') \
  528.                     and aa not in dest:
  529.                     if aa == 'print': aa = 'printer'
  530.                     dest.append(aa)
  531.  
  532.         elif o in ('-v', '--viewer'):
  533.             a = a.strip()
  534.             b = utils.which(a)
  535.             if not b:
  536.                 log.error("Viewer application not found.")
  537.             else:
  538.                 viewer = os.path.join(b, a)
  539.                 if 'viewer' not in dest:
  540.                     dest.append('viewer')
  541.  
  542.         elif o in ('-e', '--editor'):
  543.             a = a.strip()
  544.             b = utils.which(a)
  545.             if not b:
  546.                 log.error("Editor application not found.")
  547.             else:
  548.                 editor = os.path.join(b, a)
  549.                 if 'editor' not in dest:
  550.                     dest.append('editor')
  551.  
  552.         elif o == '--pdf':
  553.             a = a.strip()
  554.             b = utils.which(a)
  555.             if not b:
  556.                 log.error("PDF viewer application not found.")
  557.             else:
  558.                 pdf_viewer = os.path.join(b, a)
  559.                 if 'pdf' not in dest:
  560.                     dest.append('pdf')
  561.  
  562.         elif o in ('-p', '--printer'):
  563.             if a.startswith('*'):
  564.                 printer_name = cups.getDefaultPrinter()
  565.                 log.debug(printer_name)
  566.                 
  567.                 if printer_name is not None:
  568.                     log.info("Using CUPS default printer: %s" % printer_name)
  569.                 else:
  570.                     log.error("CUPS default printer is not set.")
  571.                 
  572.             else:
  573.                 printer_name = a
  574.             
  575.             if printer_name is not None:
  576.                 pp = printer_name.strip()
  577.                 
  578.                 printer_list = cups.getPrinters()
  579.                 found = False
  580.                 for p in printer_list:
  581.                     if p.name == pp:
  582.                         found = True
  583.                         printer = pp
  584.                         break
  585.  
  586.                 if found: 
  587.                     if 'printer' not in dest:
  588.                         dest.append('printer')
  589.                 else:
  590.                     log.error("Unknown/invalid printer name: %s" % pp)
  591.                     
  592.  
  593.         elif o == '--fax':
  594.             #print "fax"
  595.             pp = a.strip()
  596.             from prnt import cups
  597.             printer_list = cups.getPrinters()
  598.             found = False
  599.             for p in printer_list:
  600.                 if p.name == pp:
  601.                     found = True
  602.                     fax = pp
  603.                     break
  604.  
  605.             if found: 
  606.                 if 'fax' not in dest:
  607.                     dest.append('fax')
  608.             else:
  609.                 log.error("Unknown/invalid fax name: %s" % pp)
  610.  
  611.         elif o == '--email-to':
  612.             email_to = a.split(',')
  613.             if 'email' not in dest:
  614.                 dest.append('email')
  615.  
  616.         elif o == '--email-from':
  617.             email_from = a
  618.             if 'email' not in dest:
  619.                 dest.append('email')
  620.  
  621.         elif o == '--email-subject':
  622.             email_subject = a
  623.             if 'email' not in dest:
  624.                 dest.append('email')
  625.  
  626.         elif o in ('--email-note', '--email-msg'):
  627.             email_note = a
  628.             if 'email' not in dest:
  629.                 dest.append('email')
  630.  
  631.         elif o == '--resize':
  632.             a = a.replace("%", "")
  633.             try:
  634.                 resize = int(a)
  635.             except ValueError:
  636.                 resize = 100
  637.                 log.error("Invalid resize value. Using default of 100%.")
  638.  
  639.         elif o in ('-b', '--brightness'):
  640.             pass
  641.  
  642.         elif o in ('-c', '--contrast'):
  643.             try:
  644.                 contrast = int(a.strip())
  645.             except ValueError:
  646.                 log.error("Invalid contrast value. Using default of 100.")
  647.                 contrast = 100
  648.  
  649.         elif o == '--adf':
  650.             adf = True
  651.             output_type = 'pdf'
  652.  
  653.  
  654.     utils.log_title(__title__, __version__)
  655.  
  656.     if os.getuid() == 0:
  657.         log.warn("hp-scan should not be run as root.")
  658.  
  659.     if 'printer' in dest and not printer:
  660.         from prnt import cups
  661.         printer = cups.getDefaultPrinter()
  662.  
  663.         if printer is not None:
  664.             log.warn("Print destination enabled with no printer specified.")
  665.             log.warn("Using CUPS default printer '%s'." % printer)
  666.         else:
  667.             log.error("Print destination enabled with no printer specified.")
  668.             log.error("No CUPS default printer found. Disabling 'print' destination.")
  669.             dest.remove("printer")
  670.  
  671.     if 'fax' in dest and 'file' not in dest:
  672.         log.error("Fax destination not implemented. Adding 'file' destination. Use output file to fax.")
  673.         dest.append('file')
  674.  
  675.     if not dest:
  676.         log.warn("No destinations specified. Adding 'file' destination by default.")
  677.         dest.append('file')
  678.  
  679.     if 'file' in dest and not output:
  680.         log.warn("File destination enabled with no output file specified.")
  681.  
  682.         if adf:
  683.             log.info("Setting output format to PDF for ADF mode.")
  684.             output = utils.createSequencedFilename("hpscan", ".pdf")
  685.             output_type = 'pdf'
  686.         else:
  687.             if scan_mode == 'gray':
  688.                 log.info("Setting output format to PNG for greyscale mode.")
  689.                 output = utils.createSequencedFilename("hpscan", ".png")
  690.                 output_type = 'png'
  691.             else:
  692.                 log.info("Setting output format to JPEG for color/lineart mode.")
  693.                 output = utils.createSequencedFilename("hpscan", ".jpg")
  694.                 output_type = 'jpeg'
  695.  
  696.         log.warn("Defaulting to '%s'." % output)
  697.  
  698.     else:
  699.         try:
  700.             output_type = os.path.splitext(output)[1].lower()[1:]
  701.             if output_type == 'jpg':
  702.                 output_type = 'jpeg'
  703.         except IndexError:
  704.             output_type = ''
  705.  
  706.     if output_type and output_type not in ('jpeg', 'png', 'pdf'):
  707.         log.error("Invalid output file format. File formats must be 'jpeg', 'png', or 'pdf'.")
  708.         sys.exit(1)
  709.  
  710.     #sys.exit(0)
  711.     ##if scan_mode == 'gray' and output_type and output_type != 'png':
  712.     ##    log.error("Grayscale scans must be saved in PNG file format. To save in other formats, set the 'editor' destination and save the image from the editor in the desired format.")
  713.     ##    sys.exit(1)
  714.  
  715.     if adf and output_type and output_type != 'pdf':
  716.         log.error("ADF scans must be saved in PDF file format.")
  717.         sys.exit(1)
  718.  
  719.     if 'email' in dest and (not email_from or not email_to):
  720.         log.error("Email specified, but email to and/or email from address(es) were not specified.")
  721.         log.error("Disabling 'email' destination.")
  722.         dest.remove("email")
  723.  
  724.     if page_size:
  725.         units = 'mm'
  726.  
  727.     if units == 'in':
  728.         if tlx is not None: tlx = tlx * 25.4
  729.         if tly is not None: tly = tly * 25.4
  730.         if brx is not None: brx = brx * 25.4
  731.         if bry is not None: bry = bry * 25.4
  732.  
  733.     elif units == 'cm':
  734.         if tlx is not None: tlx = tlx * 10.0
  735.         if tly is not None: tly = tly * 10.0
  736.         if brx is not None: brx = brx * 10.0
  737.         if bry is not None: bry = bry * 10.0
  738.  
  739.     elif units == 'pt':
  740.         if tlx is not None: tlx = tlx * 0.3528
  741.         if tly is not None: tly = tly * 0.3528
  742.         if brx is not None: brx = brx * 0.3528
  743.         if bry is not None: bry = bry * 0.3528
  744.  
  745.     elif units == 'px':
  746.         log.warn("Units set to pixels. Using resolution of %ddpi for area calculations." % res)
  747.         if tlx is not None: tlx = tlx / res * 25.4
  748.         if tly is not None: tly = tly / res * 25.4
  749.         if brx is not None: brx = brx / res * 25.4
  750.         if bry is not None: bry = bry / res * 25.4
  751.  
  752.     if tlx is not None and brx is not None and tlx >= brx:
  753.         log.error("Invalid values for tlx (%d) and brx (%d) (tlx>=brx). Using defaults." % (tlx, brx))
  754.         tlx = brx = None
  755.  
  756.     if tly is not None and bry is not None and tly >= bry:
  757.         log.error("Invalid values for tly (%d) and bry (%d) (tly>=bry). Using defaults." % (tly, bry))
  758.         tly = bry = None
  759.  
  760.  
  761.  
  762.  
  763.     # Security: Do *not* create files that other users can muck around with
  764.     os.umask (0037)
  765.  
  766.     if not prop.scan_build:
  767.         log.error("Scanning disabled in build. Exiting")
  768.         sys.exit(1)
  769.  
  770.     if mode == GUI_MODE:
  771.         log.error("GUI mode is not implemented yet. Please use -n. Refer to 'hp-scan -h' for help.")
  772.         sys.exit(1)
  773.  
  774.         if not prop.gui_build:
  775.             log.warn("GUI mode disabled in build. Reverting to interactive mode.")
  776.             mode = NON_INTERACTIVE_MODE
  777.  
  778.         elif not os.getenv('DISPLAY'):
  779.             log.warn("No display found. Reverting to interactive mode.")
  780.             mode = NON_INTERACTIVE_MODE
  781.  
  782.         elif not utils.checkPyQtImport():
  783.             log.warn("PyQt init failed. Reverting to interactive mode.")
  784.             mode = NON_INTERACTIVE_MODE
  785.  
  786.     if mode == GUI_MODE:
  787.         app = None
  788.         sendfax = None
  789.  
  790.         from qt import *
  791.  
  792.         # UI Forms
  793.         from ui.scanform import ScanForm
  794.  
  795.         # create the main application object
  796.         app = QApplication(sys.argv)
  797.  
  798.         if loc is None:
  799.             loc = user_cfg.ui.get("loc", "system")
  800.             if loc.lower() == 'system':
  801.                 loc = str(QTextCodec.locale())
  802.                 log.debug("Using system locale: %s" % loc)
  803.  
  804.         if loc.lower() != 'c':
  805.             e = 'utf8'
  806.             try:
  807.                 l, x = loc.split('.')
  808.                 loc = '.'.join([l, e])
  809.             except ValueError:
  810.                 l = loc
  811.                 loc = '.'.join([loc, e])
  812.  
  813.             log.debug("Trying to load .qm file for %s locale." % loc)
  814.             trans = QTranslator(None)
  815.  
  816.             qm_file = 'hplip_%s.qm' % l
  817.             log.debug("Name of .qm file: %s" % qm_file)
  818.             loaded = trans.load(qm_file, prop.localization_dir)
  819.  
  820.             if loaded:
  821.                 app.installTranslator(trans)
  822.             else:
  823.                 loc = 'c'
  824.  
  825.         if loc == 'c':
  826.             log.debug("Using default 'C' locale")
  827.         else:
  828.             log.debug("Using locale: %s" % loc)
  829.             QLocale.setDefault(QLocale(loc))
  830.             prop.locale = loc
  831.             try:
  832.                 locale.setlocale(locale.LC_ALL, locale.normalize(loc))
  833.             except locale.Error:
  834.                 pass
  835.  
  836.         scanui = ScanForm(device_uri, printer_name, args) 
  837.  
  838.         app.setMainWidget(scanui)
  839.  
  840.         pid = os.getpid()
  841.         log.debug('pid=%d' % pid)
  842.  
  843.         scanui.show()
  844.  
  845.         signal.signal(signal.SIGPIPE, signal.SIG_IGN)
  846.  
  847.         try:
  848.             log.debug("Starting GUI loop...")
  849.             app.exec_loop()
  850.         except KeyboardInterrupt:
  851.             pass
  852.         #except:
  853.         #    log.exception()
  854.  
  855.     else: # NON_INTERACTIVE_MODE
  856.         import Queue
  857.         from scan import sane
  858.         import scanext
  859.         import cStringIO
  860.  
  861.         try:
  862.             import subprocess
  863.         except ImportError:
  864.             # Pre-2.4 Python
  865.             from base import subproc as subprocess
  866.  
  867.         try:
  868.             import Image
  869.         except ImportError:
  870.             log.error("'hp-scan -n' requires the Python Imaging Library (PIL). Please install it and try again or run 'hp-scan -u' instead.")
  871.             sys.exit(1)
  872.  
  873.     ##    if output_type == 'pdf':
  874.     ##        try:
  875.     ##            from reportlab.pdfgen import canvas
  876.     ##        except ImportError:
  877.     ##            log.error("PDF output requires ReportLab.")
  878.     ##            sys.exit(1)
  879.  
  880.         sane.init()
  881.         devices = sane.getDevices()
  882.  
  883.         if not device_uri:
  884.             if len(devices) == 0:
  885.                 log.error("No scanning devices found.")
  886.                 sys.exit(1)
  887.  
  888.             elif len(devices) == 1:
  889.                 device_uri = devices[0][0]
  890.  
  891.             else:
  892.                 log.info(log.bold("\nChoose device:\n"))
  893.  
  894.                 max_deviceid_size = 0
  895.                 for d in devices:
  896.                     max_deviceid_size = max(len(d[0]), max_deviceid_size)
  897.  
  898.                 formatter = utils.TextFormatter(
  899.                         (
  900.                             {'width': 4},
  901.                             {'width': max_deviceid_size, 'margin': 2},
  902.                         )
  903.                     )
  904.  
  905.                 log.info(formatter.compose(("Num.", "Device URI")))
  906.                 log.info(formatter.compose(('-'*4, '-'*(max_deviceid_size))))
  907.  
  908.                 i = 0
  909.                 for d in devices:
  910.                     log.info(formatter.compose((str(i), d[0])))
  911.                     i += 1
  912.  
  913.                 ok, x = tui.enter_range("\nEnter number 0...%d for device (q=quit) ?" % (i-1), 0, (i-1))
  914.                 if not ok: sys.exit(0)
  915.                 device_uri = devices[x][0]
  916.  
  917.  
  918.         log.info(log.bold("Using device %s" % device_uri))
  919.         log.info("Opening connection to device...")
  920.  
  921.         try:
  922.             device = sane.openDevice(device_uri)
  923.         except scanext.error, e:
  924.             log.error(e)
  925.             sys.exit(1)
  926.  
  927.         #print device.options
  928.         #sys.exit(0)
  929.  
  930.         tlx = device.getOptionObj('tl-x').limitAndSet(tlx)
  931.         tly = device.getOptionObj('tl-y').limitAndSet(tly)
  932.         brx = device.getOptionObj('br-x').limitAndSet(brx)
  933.         bry = device.getOptionObj('br-y').limitAndSet(bry)
  934.  
  935.         scan_area = (brx - tlx) * (bry - tly) # mm^2
  936.         scan_px = scan_area * res * res / 645.16 # res is in DPI
  937.  
  938.         if scan_mode == 'color':
  939.             scan_size = scan_px * 3 # 3 bytes/px
  940.         else:
  941.             scan_size = scan_px # 1 byte/px
  942.  
  943.         if scan_size > 52428800: # 50MB
  944.             if res > 600:
  945.                 log.warn("Using resolutions greater than 600 dpi will cause very large files to be created.")
  946.             else:
  947.                 log.warn("The scan current parameters will cause very large files to be created.")
  948.  
  949.             log.warn("This can cause the scan to take a long time to complete and may cause your system to slow down.")
  950.             log.warn("Approx. number of bytes to read from scanner: %s" % utils.format_bytes(scan_size, True))
  951.  
  952.         res = device.getOptionObj('resolution').limitAndSet(res)
  953.  
  954.         device.setOption('compression', scanner_compression)
  955.  
  956.         if brx - tlx <= 0.0 or bry - tly <= 0.0:
  957.             log.error("Invalid scan area (width or height is negative).")
  958.             sys.exit(1)
  959.  
  960.         log.info("")
  961.         log.info("Resolution: %ddpi" % res)
  962.         log.info("Mode: %s" % scan_mode)
  963.         log.info("Compression: %s" % scanner_compression)
  964.         log.info("Scan area (mm):")
  965.         log.info("  Top left (x,y): (%fmm, %fmm)" % (tlx, tly))
  966.         log.info("  Bottom right (x,y): (%fmm, %fmm)" % (brx, bry))
  967.         log.info("  Width: %fmm" % (brx - tlx))
  968.         log.info("  Height: %fmm" % (bry - tly))
  969.  
  970.         if page_size:
  971.             units = page_units # for display purposes only
  972.             log.info("Page size: %s" % size_desc)
  973.             if units != 'mm':
  974.                 log.note("This scan area below in '%s' units may not be exact due to rounding errors." % units)
  975.  
  976.         if units == 'in':
  977.             log.info("Scan area (in):")
  978.             log.info("  Top left (x,y): (%fin, %fin)" % (tlx/25.4, tly/25.4))
  979.             log.info("  Bottom right (x,y): (%fin, %fin)" % (brx/25.4, bry/25.4))
  980.             log.info("  Width: %fin" % ((brx - tlx)/25.4))
  981.             log.info("  Height: %fin" % ((bry - tly)/25.4))
  982.  
  983.         elif units == 'cm':
  984.             log.info("Scan area (cm):")
  985.             log.info("  Top left (x,y): (%fcm, %fcm)" % (tlx/10.0, tly/10.0))
  986.             log.info("  Bottom right (x,y): (%fcm, %fcm)" % (brx/10.0, bry/10.0))
  987.             log.info("  Width: %fcm" % ((brx - tlx)/10.0))
  988.             log.info("  Height: %fcm" % ((bry - tly)/10.0))
  989.  
  990.         elif units == 'px':
  991.             log.info("Scan area (px @ %ddpi):" % res)
  992.             log.info("  Top left (x,y): (%fpx, %fpx)" % (tlx*res/25.4, tly*res/25.4))
  993.             log.info("  Bottom right (x,y): (%fpx, %fpx)" % (brx*res/25.4, bry*res/25.4))
  994.             log.info("  Width: %fpx" % ((brx - tlx)*res/25.4))
  995.             log.info("  Height: %fpx" % ((bry - tly)*res/25.4))
  996.  
  997.         elif units == 'pt':
  998.             log.info("Scan area (pt):")
  999.             log.info("  Top left (x,y): (%fpt, %fpt)" % (tlx/0.3528, tly/0.3528))
  1000.             log.info("  Bottom right (x,y): (%fpt, %fpt)" % (brx/0.3528, bry/0.3528))
  1001.             log.info("  Width: %fpt" % ((brx - tlx)/0.3528))
  1002.             log.info("  Height: %fpt" % ((bry - tly)/0.3528))
  1003.  
  1004.         log.info("Destination(s): %s" % ', '.join(dest))
  1005.  
  1006.         if 'file' in dest:
  1007.             log.info("Output file: %s" % output)
  1008.  
  1009.         update_queue = Queue.Queue()
  1010.         event_queue = Queue.Queue()
  1011.  
  1012.         device.setOption("mode", scan_mode)
  1013.         device.setOption("resolution", res)
  1014.  
  1015.         if adf:
  1016.             try:
  1017.                 device.setOption("source", "ADF")
  1018.                 device.setOption("batch-scan", True)
  1019.             except scanext.error:
  1020.                 log.error("Failed to set ADF mode. Does this device support ADF? Disabling ADF mode.")
  1021.                 adf = False
  1022.  
  1023.         if not adf:
  1024.             try:
  1025.                 device.setOption("source", "Auto")
  1026.                 device.setOption("batch-scan", False)
  1027.             except scanext.error:
  1028.                 log.debug("Error setting source or batch-scan option (this is probably OK).")
  1029.  
  1030.         log.info("\nWarming up...")
  1031.  
  1032.         no_docs = False
  1033.         page = 1
  1034.         adf_page_files = []
  1035.         #adf_pages = []
  1036.  
  1037.         cleanup_spinner()
  1038.         log.info("")
  1039.  
  1040.         try:
  1041.             while True:
  1042.                 if adf:
  1043.                     log.info("\nPage %d: Scanning..." % page)
  1044.                 else:
  1045.                     log.info("\nScanning...")
  1046.  
  1047.                 bytes_read = 0
  1048.  
  1049.                 try:
  1050.                     try:
  1051.                         ok, expected_bytes, status = device.startScan("RGBA", update_queue, event_queue)
  1052.                     except scanext.error, e:
  1053.                         log.error(e)
  1054.                         sys.exit(1)
  1055.                     except KeyboardInterrupt:
  1056.                         log.error("Aborted.")
  1057.                         device.cancelScan()
  1058.                         sys.exit(1) 
  1059.  
  1060.                     if adf and status == scanext.SANE_STATUS_NO_DOCS:
  1061.                         if page-1 == 0:
  1062.                             log.error("No document(s). Please load documents and try again.")
  1063.                             sys.exit(0)
  1064.                         else:    
  1065.                             log.info("Out of documents. Scanned %d pages total." % (page-1))
  1066.                             no_docs = True
  1067.                             break
  1068.  
  1069.                     if adf:
  1070.                         log.info("Expecting to read %s from scanner (per page)." % utils.format_bytes(expected_bytes))
  1071.                     else:
  1072.                         log.info("Expecting to read %s from scanner." % utils.format_bytes(expected_bytes))
  1073.  
  1074.                     device.waitForScanActive()
  1075.  
  1076.                     pm = tui.ProgressMeter("Reading data:")
  1077.  
  1078.                     while device.isScanActive():
  1079.                         while update_queue.qsize():
  1080.                             try:
  1081.                                 status, bytes_read = update_queue.get(0)
  1082.  
  1083.                                 #if log.get_level() >= log.LOG_LEVEL_INFO:
  1084.                                 if not log.is_debug():
  1085.                                     pm.update(int(100*bytes_read/expected_bytes), 
  1086.                                         utils.format_bytes(bytes_read))
  1087.  
  1088.     ##                            if status == scanext.SANE_STATUS_EOF:
  1089.     ##                                log.debug("EOF")
  1090.     ##                            elif status == scanext.SANE_STATUS_NO_DOCS:
  1091.     ##                                log.debug("NO DOCS")
  1092.     ##                                no_docs = True
  1093.                                 #elif status != scanext.SANE_STATUS_GOOD:
  1094.                                 if status != scanext.SANE_STATUS_GOOD:
  1095.                                     log.error("SANE error %d" % status)
  1096.                                     sys.exit(1)
  1097.  
  1098.                             except Queue.Empty:
  1099.                                 break
  1100.  
  1101.  
  1102.                         time.sleep(0.5)
  1103.  
  1104.                 except KeyboardInterrupt:
  1105.                     log.error("Aborted.")
  1106.                     device.cancelScan()
  1107.                     sys.exit(1)
  1108.  
  1109.                 # Make sure queue is cleared out...
  1110.                 while update_queue.qsize():
  1111.                     status, bytes_read = update_queue.get(0)
  1112.  
  1113.                     if not log.is_debug():
  1114.                         pm.update(int(100*bytes_read/expected_bytes), 
  1115.                             utils.format_bytes(bytes_read))
  1116.  
  1117.     ##                if status == scanext.SANE_STATUS_EOF:
  1118.     ##                    log.debug("EOF")
  1119.     ##                elif status == scanext.SANE_STATUS_NO_DOCS:
  1120.     ##                    log.debug("NO DOCS")
  1121.     ##                    no_docs = True
  1122.  
  1123.                     if status != scanext.SANE_STATUS_GOOD:
  1124.                         log.error("SANE error %d" % status)
  1125.                         sys.exit(1)
  1126.  
  1127.                 log.info("")
  1128.  
  1129.                 if bytes_read:
  1130.                     log.info("Read %s from scanner." % utils.format_bytes(bytes_read))
  1131.  
  1132.                     buffer, format, format_name, pixels_per_line, \
  1133.                         lines, depth, bytes_per_line, pad_bytes, total_read = device.getScan()
  1134.  
  1135.                     if scan_mode in ('color', 'gray'):
  1136.                         try:
  1137.                             im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(), 
  1138.                                 'raw', 'RGBA', 0, 1)
  1139.                         except ValueError:
  1140.                             log.error("Did not read enough data from scanner (I/O Error?)")
  1141.                             sys.exit(1)
  1142.  
  1143.     ##                elif scan_mode == 'gray':
  1144.     ##                    im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(), 
  1145.     ##                        'raw', 'RGBA', 0, 1).convert('P')
  1146.  
  1147.                     elif scan_mode == 'lineart':
  1148.                         try:
  1149.                             im = Image.frombuffer('RGBA', (pixels_per_line, lines), buffer.read(), 
  1150.                                 'raw', 'RGBA', 0, 1).convert('L')
  1151.                         except ValueError:
  1152.                             log.error("Did not read enough data from scanner (I/O Error?)")
  1153.                             sys.exit(1)
  1154.  
  1155.                     if adf:
  1156.                         temp_output = utils.createSequencedFilename("hpscan_pg%d_" % page, ".png")
  1157.                         adf_page_files.append(temp_output)
  1158.                         im.save(temp_output)  
  1159.                         log.debug("Saved page %d to file %s" % (page, temp_output))
  1160.                         #adf_pages.append(im)
  1161.                 else:
  1162.                     log.error("No data read.")
  1163.                     sys.exit(1)
  1164.  
  1165.                 if not adf or (adf and no_docs):
  1166.                     break
  1167.  
  1168.                 page += 1
  1169.  
  1170.         finally:
  1171.             log.info("Closing device.")
  1172.             device.cancelScan()
  1173.             #print "0"
  1174.             #device.freeScan()
  1175.             #sane.deInit()
  1176.  
  1177.         #if no_docs:
  1178.         #    sys.exit(0)
  1179.  
  1180.         #print "1"
  1181.         if adf:
  1182.             try:
  1183.                 from reportlab.pdfgen import canvas
  1184.             except ImportError:
  1185.                 log.error("PDF output requires ReportLab.")
  1186.                 sys.exit(1)
  1187.             #print "2"
  1188.             #print canvas
  1189.             #print canvas.Canvas
  1190.  
  1191.             tlx_max = device.getOptionObj('tl-x').constraint[1]
  1192.             bry_max = device.getOptionObj('br-y').constraint[1]
  1193.  
  1194.             if not output:
  1195.                 output = utils.createSequencedFilename("hpscan", ".pdf")
  1196.  
  1197.             c = canvas.Canvas(output, (tlx_max/0.3528, bry_max/0.3528))
  1198.             #print c
  1199.  
  1200.             #for p in adf_page_files:
  1201.             for p in adf_page_files:
  1202.                 log.info("Processing page %s..." % p)
  1203.  
  1204.                 image = Image.open(p)
  1205.                 #print image
  1206.  
  1207.                 try:
  1208.                     c.drawInlineImage(image, (tlx/0.3528), ((bry_max/0.3528)-(bry/0.3528)), 
  1209.  
  1210.                     #c.drawInlineImage(image, 0, bry/0.3528,
  1211.                         width=None,height=None)
  1212.                 except NameError:
  1213.                     log.error("A problem has occurred with PDF generation. This is a known bug in ReportLab. Please update your install of ReportLab to version 2.0 or greater.")
  1214.                     sys.exit(1)
  1215.  
  1216.                 c.showPage()
  1217.  
  1218.             log.info("Saving to file %s" % output)
  1219.             c.save()
  1220.             log.info("Viewing PDF file in %s" % pdf_viewer)
  1221.             os.system("%s %s &" % (pdf_viewer, output))            
  1222.  
  1223.  
  1224.             sys.exit(0)
  1225.  
  1226.         if resize != 100:
  1227.             if resize < 1 or resize > 400:
  1228.                 log.error("Resize parameter is incorrect. Resize must be 0% < resize < 400%.")
  1229.                 log.error("Using resize value of 100%.")
  1230.             else:
  1231.                 new_w = pixels_per_line * resize / 100
  1232.                 new_h = lines * resize / 100
  1233.                 log.info("Resizing from %dx%d to %dx%d..." % (pixels_per_line, lines, new_w, new_h))
  1234.                 im = im.resize((new_w, new_h), Image.ANTIALIAS)
  1235.  
  1236.         file_saved = False
  1237.         if 'file' in dest:
  1238.             log.info("\nOutputting to destination 'file':")
  1239.             log.info("Saving to file %s" % output)
  1240.  
  1241.             try:
  1242.                 im.save(output)
  1243.             except IOError, e:
  1244.                 log.error("Error saving file: %s (I/O)" % e)
  1245.                 try:
  1246.                     os.remove(output)
  1247.                 except OSError:
  1248.                     pass
  1249.                 sys.exit(1)
  1250.             except ValueError, e:
  1251.                 log.error("Error saving file: %s (PIL)" % e)
  1252.                 try:
  1253.                     os.remove(output)
  1254.                 except OSError:
  1255.                     pass
  1256.                 sys.exit(1)
  1257.  
  1258.             file_saved = True
  1259.             dest.remove("file")
  1260.  
  1261.         temp_saved = False
  1262.         if ('editor' in dest or 'viewer' in dest or 'email' in dest or 'printer' in dest) \
  1263.             and not file_saved:
  1264.  
  1265.             output_fd, output = utils.make_temp_file(suffix='.png')
  1266.             try:
  1267.                 im.save(output)
  1268.             except IOError, e:
  1269.                 log.error("Error saving temporary file: %s" % e)
  1270.  
  1271.                 try:
  1272.                     os.remove(output)
  1273.                 except OSError:
  1274.                     pass
  1275.  
  1276.                 sys.exit(1)
  1277.  
  1278.             os.close(output_fd) 
  1279.             temp_saved = True      
  1280.  
  1281.         for d in dest:
  1282.             log.info("\nSending to destination '%s':" % d)
  1283.  
  1284.             if d == 'fax':
  1285.                 log.error("fax: Not implemented yet.")
  1286.  
  1287.             elif d == 'pdf':
  1288.                 try:
  1289.                     from reportlab.pdfgen import canvas
  1290.                 except ImportError:
  1291.                     log.error("PDF output requires ReportLab.")
  1292.                     continue
  1293.  
  1294.                 tlx_max = device.getOptionObj('tl-x').constraint[1]
  1295.                 bry_max = device.getOptionObj('br-y').constraint[1]
  1296.  
  1297.                 pdf_output = utils.createSequencedFilename("hpscan", ".pdf")
  1298.                 c = canvas.Canvas(pdf_output, (tlx_max/0.3528, bry_max/0.3528))
  1299.  
  1300.                 try:
  1301.                     c.drawInlineImage(im, (tlx/0.3528), ((bry_max/0.3528)-(bry/0.3528)), 
  1302.                         width=None,height=None)
  1303.                 except NameError:
  1304.                     log.error("A problem has occurred with PDF generation. This is a known bug in ReportLab. Please update your install of ReportLab to version 2.0 or greater.")
  1305.                     continue
  1306.  
  1307.                 c.showPage()
  1308.                 log.info("Saving to file %s" % pdf_output)
  1309.                 c.save()
  1310.                 log.info("Viewing PDF file in %s" % pdf_viewer)
  1311.                 os.system("%s %s &" % (pdf_viewer, pdf_output))
  1312.  
  1313.             elif d == 'printer':
  1314.                 hp_print = utils.which("hp-print")
  1315.                 if hp_print:
  1316.                     cmd = 'hp-print %s &' % output
  1317.                 else:
  1318.                     cmd = "python ./print.py %s &" % output
  1319.  
  1320.                 os.system(cmd)
  1321.  
  1322.             elif d == 'email':
  1323.                 try:
  1324.                     from email.mime.image import MIMEImage
  1325.                     from email.mime.multipart import MIMEMultipart
  1326.                     from email.mime.text import MIMEText
  1327.                 except ImportError:
  1328.                     try:
  1329.                         from email.MIMEImage import MIMEImage
  1330.                         from email.MIMEMultipart import MIMEMultipart
  1331.                         from email.MIMEText import MIMEText
  1332.                     except ImportError:
  1333.                         log.error("hp-scan email destination requires Python 2.2+.")
  1334.                         continue
  1335.  
  1336.                 msg = MIMEMultipart()
  1337.                 msg['Subject'] = email_subject
  1338.                 msg['From'] = email_from
  1339.                 msg['To'] = ','.join(email_to)
  1340.                 msg.preamble = 'Scanned using hp-scan'
  1341.  
  1342.                 if email_note:
  1343.                     txt = MIMEText(email_note)
  1344.                     msg.attach(txt)
  1345.  
  1346.                 if file_saved:
  1347.                     txt = MIMEText("attached: %s: %dx%d %s PNG image." % 
  1348.                         (os.path.basename(output), pixels_per_line, lines, scan_mode))
  1349.                 else:
  1350.                     txt = MIMEText("attached: %dx%d %s PNG image." % (pixels_per_line, lines, scan_mode))
  1351.  
  1352.                 msg.attach(txt) 
  1353.  
  1354.                 fp = open(output, 'r')
  1355.                 img = MIMEImage(fp.read())
  1356.                 fp.close()
  1357.  
  1358.                 if file_saved:
  1359.                     img.add_header('Content-Disposition', 'attachment', filename=os.path.basename(output))
  1360.  
  1361.                 msg.attach(img)
  1362.  
  1363.                 sendmail = utils.which("sendmail")
  1364.  
  1365.                 if sendmail:
  1366.                     sendmail = os.path.join(sendmail, 'sendmail')
  1367.                     cmd = [sendmail,'-t','-r',email_from]
  1368.  
  1369.                     log.debug(repr(cmd))
  1370.                     err = None
  1371.                     try:
  1372.                         sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  1373.                         std_out, std_err = sp.communicate(msg.as_string())
  1374.                         if std_err != '':
  1375.                             err = std_err
  1376.                     except OSError, e:
  1377.                         err = str(e)
  1378.                     cleanup_spinner()
  1379.  
  1380.                     if err:
  1381.                         log.error(repr(err))
  1382.  
  1383.                 else:
  1384.                     log.error("Mail send failed. 'sendmail' not found.")
  1385.  
  1386.             elif d == 'viewer':
  1387.                 if viewer:
  1388.                     log.info("Viewing file in %s" % viewer)
  1389.                     os.system("%s %s &" % (viewer, output))
  1390.                 else:
  1391.                     log.error("Viewer not found.")
  1392.  
  1393.             elif d == 'editor':
  1394.                 if editor:
  1395.                     log.info("Editing file in %s" % editor)
  1396.                     os.system("%s %s &" % (editor, output))
  1397.                 else:
  1398.                     log.error("Editor not found.")
  1399.  
  1400.         device.freeScan()
  1401.         sane.deInit()    
  1402.  
  1403.  
  1404. except KeyboardInterrupt:
  1405.     log.error("User exit")
  1406.  
  1407. log.info("")
  1408. log.info("Done.")
  1409.  
  1410.